#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>

#include "nce_socket.h"

namespace nce
{

#define LISTENQ 	5
#define MAXRECVLEN 	1024

int Socket::open_clientfd(const char *hostname, int port)
{
    int clientfd;
    struct hostent *hp;
    struct sockaddr_in serveraddr;

    if ((clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        return -1;

    if ((hp = gethostbyname(hostname)) == NULL)
        return -2;

    memset((char *) &serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    memcpy((char *)hp->h_addr, (char *)&serveraddr.sin_addr.s_addr,
        hp->h_length);
    serveraddr.sin_port = htons(port);

    if (connect(clientfd, (struct sockaddr *)&serveraddr, 
        sizeof(serveraddr)) < 0)
        return -1;
    
    return clientfd;
}

int Socket::open_listenfd(int port)
{
    int listenfd, optval=1;
    struct sockaddr_in serveraddr;

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        return -1;

    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, 
        (const void *)&optval, sizeof(int)) < 0)
        return -1;

    memset((char *)&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons((unsigned short)port);

    if (bind(listenfd, (struct sockaddr *)&serveraddr, 
        sizeof(serveraddr)) < 0)
        return -1;

    if (listen(listenfd, LISTENQ) < 0)
        return -1;

    return listenfd;
}

int Socket::send(const char *buffer, size_t length)
{
	write(_socket, buffer, length); 

	return 0;
}

int Socket::send(OutputBuffer &ob)
{
	char *buf = new char[ob.getLen()+sizeof(size_t)];
	size_t len = ob.getLen();
	*(size_t *)buf = len;
	memcpy(buf+sizeof(size_t), ob.getBuf(), len);
	send(buf, len+sizeof(size_t));
	
	delete [] buf;
	return 0;
}

int Socket::recv(char *buffer, size_t &length)
{
	length = read(_socket, buffer, MAXRECVLEN); 
	
	return 0;
}

int Socket::recv(InputBuffer &ib)
{
	char *buf = new char[MAXRECVLEN];
	size_t len;
		
    read(_socket, buf, sizeof(int));
    len = *(size_t *)buf;
    
	recv(buf, len);
	ib.setBuf(buf, len);
	
	return 0;
}

int ServerSocket::start(Servant *servant)
{
	int listenfd = open_listenfd(_port);
	while (true) 
	{
		_socket = accept(listenfd, NULL, NULL);
		
		servant->doServ();	// callback
		
		close(_socket);
	}

    return 0;
}

}
